# -*-coding:Utf-8 -*
"""@package core
@b Sealed Zone Inputs module
@brief Ce module permet la gestion des entrés utilisateur.

"""
import bge, mathutils
from .common import *

def isActive(state):
	""" Fonction qui est vrai lorsque l'état donné est actif ( l'état KX_INPUT_JUST_RELEASED est compté comme inactif )
	@param state État à tester """
	if state in [bge.logic.KX_INPUT_JUST_ACTIVATED, bge.logic.KX_INPUT_ACTIVE]:
		return True
	else:
		return False

def isChange(state):
	""" Fonction qui est vrai lorsque l'état donné change ( exemple : pour une touche, renvoi True si la touche viens d'être enfoncé ou relaché )
	@param state État à tester """
	if state in [bge.logic.KX_INPUT_JUST_ACTIVATED, bge.logic.KX_INPUT_JUST_RELEASED]:
		return True
	else:
		return False

HAT_UP = 1
HAT_DOWN = 4
HAT_LEFT = 8
HAT_RIGHT = 2
HAT_UP_LEFT = 9
HAT_UP_RIGHT = 3
HAT_DOWN_LEFT = 12
HAT_DOWN_RIGHT = 6
HAT_UP_DOWN = 5
HAT_LEFT_RIGHT = 10

def HAT(num, co):
	return 'ha'+str(num)+':'+str(co)
def BUTTON(num):
	return 'bu'+str(num)
def AXIS(num):
	return 'ax'+str(num)

## @private	
class JoystickControl(object, metaclass=ClassEvent):
	
	ref_joy = {}
	activeEvents = ()
	
	@classmethod
	def update(cls, joysticks):
		activeEvt = []
		for numJoy in cls.ref_joy:
			joystick = joysticks[numJoy]
			if joystick == None:
				continue
			for aButton in cls.ref_joy[numJoy]['bu']:
				if aButton.setActivity(aButton.num in joystick.activeButtons):
					activeEvt.append(aButton)
			for anAxis in cls.ref_joy[numJoy]['ax']:
				if anAxis.setActivity(joystick.axisValues[anAxis.num]):
					activeEvt.append(anAxis)
			for aHat in cls.ref_joy[numJoy]['ha']:
				if aHat.setActivity(joystick.hatValues[aHat.num]):
					activeEvt.append(aHat)
		cls.activeEvents = tuple(activeEvt)
		
class JoystickEvent(object):
	""" @brief Event concernant les joystick/manettes de jeu.
	Les instances de cette classe  sont utiliser 
	avec la classe Input de la même manière qu'une touche ( grace au paramètre "key" )
	"""
	
	HAT_UP = (1,3,9) 
	HAT_DOWN = (4,6,12)
	HAT_LEFT = (8,9,12)
	HAT_RIGHT = (2,3,6)
	
	def __new__(cls, joy,code):
		"""@private"""
		result = None
		etype = code[:2]
		joy = int(joy)
		if etype == 'ha':
			tmp = code[2:].split(':')
			di = int(tmp[1])
			num = int(tmp[0])
			if joy in JoystickControl.ref_joy:
				for aEv in JoystickControl.ref_joy[joy][etype]:
					if aEv.num == num and aEv.direction == di:
						result = aEv
		else:
			num = int(code[2:])
			if joy in JoystickControl.ref_joy:
				for aEv in JoystickControl.ref_joy[joy][etype]:
					if aEv.num == num:
						result = aEv
		if result == None:
			result = object.__new__(cls)
			result.init = False
			result.num = num
			if etype == 'ha':
				result.direction = di
		if not joy in JoystickControl.ref_joy and joy < len(bge.logic.joysticks):
			JoystickControl.ref_joy[joy] = { 'ha':[], 'bu':[], 'ax':[] }
		if joy in JoystickControl.ref_joy:
			JoystickControl.ref_joy[joy][etype].append(result)
		return result
		
	def __init__(self, joy, code):
		""" Pour initialiser une instance il faut utiliser 
		les fonctions et variable global du module core.inputs 
		pour récupérer le code des évènements. 
		
		@param joy Indice du joystick/manette de jeu.
		@param code Code correspondant à l'évènement.
		
		exemple :
		@code
		js_button1_event = JoystickEvent(0,BUTTON(1))
		js_hat_up_event = JoystickEvent(0,HAT(0,HAT_UP))
		@endcode
		"""
		if not self.init:
			self.setActivity = {'ha':self._setActHat, 
				'bu':self._setActButton, 
				'ax':self._setActAxis}[code[:2]]
			if code[:2] == 'ax' or (code[:2] == 'ha' and self.direction in (5,10)):
				self._axis_state = False
			self.active = 0.0
			self.init = True
			self.state = bge.logic.KX_INPUT_NONE
			
	def __bool__(self):
		"""@private"""
		if self.active != 0.0:
			return True
		return False
		
	
	def set_axis_state(self, value):
		"""@private"""
		if hasattr(self,"_axis_state"):
			self.axis_state = bool(value)
	
	def setActivity(self, value):
		"""@private"""
		return False
		
	def _setActHat(self, value):
		"""@private"""
		ret = 0.0
		if self.direction == 5:
			if value in HAT_UP:
				ret = 1.0 if value == 1 else 0.707
			elif value in HAT_DOWN:
				ret = -1.0 if value == 4 else -0.707
			else:
				ret = .0
		elif self.direction == 10:
			if value in HAT_RIGHT:
				ret = 1.0 if value == 2 else 0.707
			elif value in HAT_LEFT:
				ret = -1.0 if value == 8 else -0.707
			else:
				ret = .0
		else:
			if value == self.direction:
				ret = 1.0
		
		self.set_axis_state(False)
		self.state = bge.logic.KX_INPUT_ACTIVE
		if ret != self.active:
			self.set_axis_state(True)
			if self.active == 0.0:
				self.state = bge.logic.KX_INPUT_JUST_ACTIVATED
			self.active = ret
			if self.active == 0.0:
				self.state = bge.logic.KX_INPUT_JUST_RELEASED
			return True
		elif self.active == 0.0:
			self.state = bge.logic.KX_INPUT_NONE
			return False
		return True
			
	def _setActAxis(self, value):
		"""@private"""
		self.state = bge.logic.KX_INPUT_ACTIVE
		self.axis_state = False
		if value != self.active:
			self.axis_state = True
			if self.active == 0.0:
				self.state = bge.logic.KX_INPUT_JUST_ACTIVATED
			self.active = value
			if self.active == 0.0:
				self.state = bge.logic.KX_INPUT_JUST_RELEASED
			return True
		elif self.active == 0.0:
			self.state = bge.logic.KX_INPUT_NONE
			return False
		return True
	def _setActButton(self, value):
		"""@private"""
		if value:
			value = 1.0
		self.state = bge.logic.KX_INPUT_NONE
		if value != self.active:
			self.active = value
			self.state = bge.logic.KX_INPUT_JUST_RELEASED
			if self.active:
				self.state = bge.logic.KX_INPUT_JUST_ACTIVATED
			return True
		elif self.active:
			self.state = bge.logic.KX_INPUT_ACTIVE
			return True
		return False
			
		
class MouseTracking(object, metaclass=ClassEvent):
	""" Classe de gestion de la souris """
	virtual_mouse = mathutils.Vector((.0,.0))
	virtual_mouse_active = True
	last_mouse_position = (0.0,0.0)
	axis_vector = mathutils.Vector((.0,.0))
	override_mouse = False
	show_mouse  = False
	threshold = 0.001
	bge.render.showMouse(show_mouse)
	
	@classmethod
	def keepVirtualMouse(cls):
		"""@private"""
		if cls.virtual_mouse.x > 1.0:
			cls.virtual_mouse.x = 1.0
		elif cls.virtual_mouse.x < 0.0:
			cls.virtual_mouse.x = 0.0
		if cls.virtual_mouse.y > 1.0:
			cls.virtual_mouse.y = 1.0
		elif cls.virtual_mouse.y < 0.0:
			cls.virtual_mouse.y = 0.0
	
	@classmethod
	def setMouse(cls, x = None, y = None):
		"""@private"""
		if type(x) == int:
			x = float(x/bge.render.getWindowWidth())
		if type(y) == int:
			y = float(y/bge.render.getWindowHeight())
		pos = bge.logic.mouse.position
		lpos = list(cls.last_mouse_position)
		if type(x) == float:
			cls.virtual_mouse.x	= x
			lpos[0] = x-(pos[0]-lpos[0])
			pos[0] = x
		if type(y) == float:
			cls.virtual_mouse.y	= y
			lpos[1] = y-(pos[1]-lpos[1])
			pos[1] = y
		cls.keepVirtualMouse()
		cls.last_mouse_position = tuple(lpos)
		bge.logic.mouse.position = pos[0],pos[1]
		"""bge.render.setMousePosition(round(bge.render.getWindowWidth()*pos[0]),
									round(bge.render.getWindowHeight()*pos[1]))"""
		
	@classmethod
	def update(cls, mouse):
		"""@private"""
		pos = mouse.position[0]/bge.render.getWindowWidth(),mouse.position[1]/bge.render.getWindowHeight()
		if cls.override_mouse:
			cls.axis_vector.x = pos[0]-0.5
			if -cls.threshold < cls.axis_vector.x < cls.threshold:
				cls.axis_vector.x = 0.0
			cls.axis_vector.y = pos[1]-0.5
			if -cls.threshold < cls.axis_vector.y < cls.threshold:
				cls.axis_vector.y = 0.0
			if cls.virtual_mouse_active:
				cls.virtual_mouse.x += cls.axis_vector.x
				cls.virtual_mouse.y += cls.axis_vector.y
			#mouse.position = 0.5,0.5
			#print(mouse.position)
			bge.render.setMousePosition(round(bge.render.getWindowWidth()*0.5),
										round(bge.render.getWindowHeight()*0.5))
		else:
			cls.axis_vector.x = pos[0]-cls.last_mouse_position[0]
			cls.axis_vector.y = pos[1]-cls.last_mouse_position[1]
			#"""
			if not cls.show_mouse:
				if not(0.0 <= pos[0] <= 1.0) or not(0.0 <= pos[1] <= 1.0):
					if not bge.logic.mouse.visible:
						print("Show!")
						bge.render.showMouse(True)
				elif bge.logic.mouse.visible:
					bge.render.showMouse(False)#"""
			if cls.virtual_mouse_active:
				cls.virtual_mouse.x = pos[0]
				cls.virtual_mouse.y = pos[1]
			cls.last_mouse_position = tuple(pos)
		if cls.virtual_mouse_active:
			cls.keepVirtualMouse()
			
	def __init__(self, offsetx = None, offsety = None):
		self.offset = (offsetx,offsety)
	
	
	@property	
	def axex(self):
		"""@property axex
		(READ ONLY) Contient le déplacement en X de la souris depuis la dernière frame
		"""
		if type(self.offset[0]) == float:
			return MouseTracking.axis_vector.x+self.offset[0]
		elif type(self.offset[0]) == int:
			 return round(MouseTracking.axis_vector.x*self.offset[0])
		else:
			return MouseTracking.axis_vector.x
	
	@property	
	def axey(self):
		"""@property axey
		(READ ONLY) Contient le déplacement en Y de la souris depuis la dernière frame
		"""
		if type(self.offset[1]) == float:
			return MouseTracking.axis_vector.y+self.offset[1]
		elif type(self.offset[1]) == int:
			 return round(MouseTracking.axis_vector.y*self.offset[1])
		else:
			return MouseTracking.axis_vector.y
			
	@property	
	def virtualx(self):
		"""@property virtualx
		Contient la position en X de la souris virtuel
		"""
		return MouseTracking.virtual_mouse.x
	@virtualx.setter
	def virtualx(self,value):
		"""@private"""
		if .0 <= value <= 1.0:
			MouseTracking.virtual_mouse.x = value
	
	@property	
	def virtualy(self):
		"""@property virtualy
		Contient la position en Y de la souris virtuel
		"""
		return MouseTracking.virtual_mouse.y
	@virtualy.setter
	def virtualy(self,value):
		"""@private"""
		if .0 <= value <= 1.0:
			MouseTracking.virtual_mouse.y = value
	
	
	@property
	def visible(self):
		"""@property visible
		Représente la visibilité de la souris
		"""
		return MouseTracking.show_mouse 
	
	@visible.setter
	def visible(self, val):
		"""@private"""
		if val:
			MouseTracking.show_mouse = True
			self.override = False
		else:
			MouseTracking.show_mouse = False
		bge.render.showMouse(MouseTracking.show_mouse)
		
	@property
	def override(self):
		"""@property override
		État du mode override :
		Si l'override est activé la souris est masquée 
		et est maintenue au centre de la fenêtre du jeu.
		Au moment ou l'override est activé, la position de la souris 
		est sauvegardé pour être restauré à cette position 
		une fois l'override désactiver.
		"""
		return MouseTracking.override_mouse
	
	@override.setter
	def override(self, val):
		"""@private"""
		if val:
			if not MouseTracking.override_mouse:
				MouseTracking.override_mouse = True
				MouseTracking.show_mouse = False
				bge.render.showMouse(False)
				MouseTracking.last_mouse_position = (bge.logic.mouse.position[0],bge.logic.mouse.position[0])
				MouseTracking.virtual_mouse = mathutils.Vector(MouseTracking.last_mouse_position)
				"""bge.render.setMousePosition(round(bge.render.getWindowWidth()*0.5),
											round(bge.render.getWindowHeight()*0.5))"""
				MouseTracking.axis_vector.x = 0.0
				MouseTracking.axis_vector.y = 0.0
				bge.logic.mouse.position = 0.5,0.5
		elif MouseTracking.override_mouse:
			MouseTracking.override_mouse = False
			bge.render.setMousePosition(
			round(bge.render.getWindowWidth()*MouseTracking.virtual_mouse.x),
			round(bge.render.getWindowHeight()*MouseTracking.virtual_mouse.y))
			
	@property
	def activeVirtual(self):
		"""@property activeVirtual
		"""
		return MouseTracking.virtual_mouse_active
	
	@activeVirtual.setter
	def activeVirtual(self, val):
		"""@private"""
		if val:
			MouseTracking.virtual_mouse_active = True
		else:
			MouseTracking.virtual_mouse_active = False
		
	def show(self):
		""" Affiche la souris
		"""
		self.visible = True
		MouseTracking.virtual_mouse_active = True
		
	def hide(self):
		""" Cache la souris
		"""
		self.visible = False
		MouseTracking.virtual_mouse_active = False

class InputMap(BasicEvent):
	
	solo_inpmap = [] 
	
	def __init__(self):
		self.__inputs_map__ = {}
		BasicEvent.__init__(self)
		self.update = self.base_update
		self._callbacks = []
		self._logs = []
		
	@property
	def solo(self):
		if InputMap.solo_inpmap == [] or self in InputMap.solo_inpmap:
			return True
		return
	@solo.setter
	def solo(self,value):
		if value and self not in InputMap.solo_inpmap:
			InputMap.solo_inpmap.append(self)
		elif not value and self in InputMap.solo_inpmap:
			InputMap.solo_inpmap.remove(self)
	
	def add_callbacks(self, *func):
		for aFu in func:
			if callable(aFu):
				self._callbacks.append(aFu)
	
	def addInput(self, inp,*keys):
		if isinstance(inp,Input):
			for k in keys:
				self.__inputs_map__[k] = inp
	
	def delInput(self, key, inp = None):
		if key in self.__inputs_map__ and (inp == None or self.__inputs_map__[key] == inp):
			del self.__inputs_map__[key]
	
	def getInput(self, key):
		if key in self.__inputs_map__:
			return self.__inputs_map__[key]
			
	def getKey(self, inp, allowList = False):
		results = []
		for aK in self.__inputs_map__:
			if self.__inputs_map__[aK] == inp:
				if not allowList:
					return aK
				results.append(aK)
		if results:
			return results
		return False
			
	def stop_all(self):
		for aKey in bge.logic.keyboard.active_events.copy():
			if aKey in self.__inputs_map__.keys():
				self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
		for aKey in bge.logic.mouse.active_events.copy():
			if aKey in self.__inputs_map__.keys():
				self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
				
	def start_all(self):
		for aKey in bge.logic.keyboard.active_events.copy():
			if aKey in self.__inputs_map__.keys() and isActive(bge.logic.keyboard.active_events[aKey]):
				self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_ACTIVATED)
		for aKey in bge.logic.mouse.active_events.copy():
			if aKey in self.__inputs_map__.keys() and isActive(bge.logic.mouse.active_events[aKey]):
				self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_ACTIVATED)
			
	def not_solo_update(self):
		if self.solo:
			self.start_all()
			self.update = self.base_update
			
	def base_update(self):
		if not self.solo:
			self.stop_all()
			self.update = self.not_solo_update
		self._callbacks = []
		for aKey in bge.logic.keyboard.active_events.copy():
			if aKey in self.__inputs_map__.keys():
				self.__inputs_map__[aKey].update(aKey, bge.logic.keyboard.active_events[aKey])
			if bge.logic.keyboard.active_events[aKey] == bge.logic.KX_INPUT_JUST_ACTIVATED:
				for log in self._logs:
					log.update(aKey)
		for aKey in bge.logic.mouse.active_events.copy():		
			if aKey in self.__inputs_map__.keys():
				self.__inputs_map__[aKey].update(aKey, bge.logic.mouse.active_events[aKey])
		for aKey in JoystickControl.activeEvents:
			if aKey in self.__inputs_map__.keys():
				self.__inputs_map__[aKey].update(aKey, aKey.state)
		for aFu in self._callbacks:
			aFu()
	
	@property
	def paused(self):
		if not self.active and hasattr(self, "_activeWhenPaused"):
			return True
		return False
	
	def pause(self):
		if hasattr(self, "_sceneWhenPaused") or hasattr(self, "_activeWhenPaused"):
			return
		self._sceneWhenPaused = bge.logic.getCurrentScene()
		self._sceneWhenPaused.post_draw.append(self._pause)
		
	def _pause(self):
		self._sceneWhenPaused.post_draw.remove(self._pause)
		del self._sceneWhenPaused
		self._EV_PreviousEvent._EV_NextEvent = self._EV_NextEvent
		self._EV_NextEvent._EV_PreviousEvent = self._EV_PreviousEvent
		self._activeWhenPaused = []
		for aKey in self.__inputs_map__:
			if self.__inputs_map__[aKey]:
				self._activeWhenPaused.append(aKey)
		self.active = False
		
	def resume(self):
		if not hasattr(self, "_activeWhenPaused"):
			return
		self.activate()
		kEvt = bge.logic.keyboard.events
		mEvt = bge.logic.mouse.events
		for aKey in self.__inputs_map__.keys():
			if aKey in kEvt.keys():
				if isActive(kEvt[aKey]) and not aKey in self._activeWhenPaused:
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_ACTIVATED)
				elif aKey in self._activeWhenPaused and not isActive(kEvt[aKey]):
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
			elif aKey in mEvt.keys():
				if isActive(mEvt[aKey]) and not aKey in self._activeWhenPaused:
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_ACTIVATED)
				elif aKey in self._activeWhenPaused and not isActive(mEvt[aKey]):
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
			elif type(aKey) == JoystickEvent:
				if aKey and not aKey  in self._activeWhenPaused:
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_ACTIVATED)
				elif aKey  in self._activeWhenPaused and not aKey:
					self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
		del self._activeWhenPaused
			
	def stop(self):
		self._EV_PreviousEvent._EV_NextEvent = self._EV_NextEvent
		self._EV_NextEvent._EV_PreviousEvent = self._EV_PreviousEvent
		for aKey in self.__inputs_map__.keys():
			self.__inputs_map__[aKey].update(aKey, bge.logic.KX_INPUT_JUST_RELEASED)
		self.active = False

class Keylog(object):
	
	def __init__(self, inpMap = False):
		self._log = ''
		self._capslock = False
		self.inpMap = inpMap
		if inpMap != False:
			inpMap._logs.append(self)
		
	def update(self,value):
		if value == bge.events.CAPSLOCKKEY:
			self._capslock = not(self._capslock)
		else:
			self._log += bge.events.EventToCharacter(value,self.shift)
	
	def setMap(self,inpMap = False):
		if self.inpMap != False:
			self.inpMap._logs.remove(self)
		self.inpMap = inpMap
		if self.inpMap != False:
			inpMap._logs.append(self)
		
	def clean(self):
		self.setMap()
		self._log = ''
		self._capslock = False
		
	def getLog(self):
		ret = self._log
		self._log = ''
		return ret
	
	@property
	def log(self):
		return self._log
	@property	
	def shift(self):
		return bge.logic.keyboard.events[bge.events.RIGHTSHIFTKEY] or bge.logic.keyboard.events[bge.events.LEFTSHIFTKEY] or self._capslock

class TextInput(Keylog):
	
	def __init__(self, inpMap = False):
		Keylog.__init__(self, inpMap)
		self._cursor = 0
		
	def update(self,value):
		if value == bge.events.BACKSPACEKEY:
			if self._log and self._cursor > 0:
				log = self._log[:self._cursor-1]
				if len(self._log) > self._cursor:
					log +=  self._log[self._cursor:]
				self._log = log
				self._cursor -= 1
				
		elif value in (bge.events.LEFTARROWKEY,bge.events.RIGHTARROWKEY):
			self._cursor += { bge.events.LEFTARROWKEY:-1, bge.events.RIGHTARROWKEY:1 }[value]
			if self._cursor > len(self._log):
				self._cursor = len(self._log)
			elif self._cursor < 0:
				self._cursor = 0
		elif value in (bge.events.DOWNARROWKEY,bge.events.UPARROWKEY):
			pass
		elif value == bge.events.CAPSLOCKKEY:
			self._capslock = not(self._capslock)
		else:
			if self._cursor == len(self._log):
				self._log += bge.events.EventToCharacter(value,self.shift)
			else:
				self._log = self._log[:self._cursor]+bge.events.EventToCharacter(value,self.shift)+self._log[self._cursor:]
			self._cursor += 1
			
	def clean(self):
		self.setMap()
		self._log = ''
		self._capslock = False
		self._cursor = 0
		
	def getLog(self):
		ret = self._log
		self._log = ''
		self._cursor = 0
		return ret
		
	@property
	def log(self):
		if self._cursor == len(self._log):
			self._log += '|'
		else:
			self._log = self._log[:self._cursor]+"|"+self._log[self._cursor:]
		
class Input(object):
	
	def __init__(self, inpMap, *keys):
		self.callbacks = []
		self.inpMap = inpMap
		self._active_keys_ = []
		if keys:
			inpMap.addInput(self, *keys)
		
	def __len__(self):
		return len(self._active_keys_)
	
	@property	
	def activity(self):
		if self._active_keys_:
			return  1.0
		return 0.0
	
	def addKey(self, *keys):
		self.inpMap.addInput(self, *keys)
		return self
	
	def delKey(self, *keys):
		for k in keys:
			self.inpMap.delInput(k,self)
			if k in self._active_keys_:
				self.update(k,bge.logic.KX_INPUT_JUST_RELEASED)
		return self
		
	def addAct(self, *acts):
		for func in acts:
			if callable(func) and func not in self.callbacks:
				self.callbacks.append(func)
		return self
				
	def delAct(self, *acts):
		for func in acts:
			if func in self.callbacks:
				self.callbacks.remove(func)
		return self
	
	def callback(self, active):
		self.level = active
		for func in self.callbacks:
			func(active)
	
	def update(self, key, state):
		if isChange(state):
			if state == bge.logic.KX_INPUT_JUST_ACTIVATED and key not in self._active_keys_:
				if len(self._active_keys_) == 0:
					self.callback(1.0)
				self._active_keys_.append(key)
			elif state == bge.logic.KX_INPUT_JUST_ACTIVATED:
				self._active_keys_.remove(key)
				if len(self._active_keys_) == 0:
					self.callback(0.0)
			elif state == bge.logic.KX_INPUT_JUST_RELEASED and key in self._active_keys_:
				if len(self._active_keys_) == 1:
					self.callback(0.0)
				self._active_keys_.remove(key)
			
				
class AxisInput(Input):
	
	def __init__(self, inpMap, axis):
		self.callbacks = []
		self.inpMap = inpMap
		if not hasattr(axis,"axis_state"):
			raise TypeError("given axis object has no axis_state attribute")
		self._axis = axis
		inpMap.addInput(self, axis)
	
	def __bool__(self):
		return bool(self._axis)
	
	def __len__(self):
		return 0
	
	def addKey(self, *keys):
		pass
	
	def delKey(self, *keys):
		pass
		
	@property
	def axis(self):
		return self._axis
	@axis.setter
	def axis(self, axis):
		if axis != self._axis and hasattr(axis,"axis_state"):
			self.inpMap.delInput( self._axis, self)
			self._axis = axis
			inpMap.addInput(self, axis)
			
	@property
	def activity(self):
		self._axis.active
		
	def update(self, key, state):
		if self._axis.axis_state:
			self.callback(self._axis.active)
			
		
